#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
NSA5312 Serial Protocol Handler
串口协议处理模块

支持的MCU协议特性：
- 设备地址：0xE1
- 9位接收模式，8位发送模式
- CRC校验（生成多项式：0x07）
- 命令帧格式：[地址][长度][命令][参数...][CRC]
- 响应帧格式：[地址][长度][命令][状态][数据...][CRC]
"""

import struct
import threading
import queue
import time
from enum import Enum
from typing import List, Optional, Tuple, Union

class CommandCode(Enum):
    """命令代码枚举"""
    # 设备信息命令
    GET_VERSION = 0x01
    
    # GPIO控制命令
    ROW_CONTROL = 0x0A
    COL_CONTROL = 0x0B
    POWER_8CH_CONTROL = 0x20
    VOLTAGE_SWITCH = 0x21
    INTERFACE_SWITCH = 0x22
    
    # 通信接口命令
    I2C_WRITE = 0x10
    I2C_READ = 0x11
    SPI_WRITE = 0x12
    SPI_READ = 0x13
    SPI4_WRITE = 0x14
    SPI4_READ = 0x15
    OWI_WRITE = 0x1A
    OWI_READ = 0x1B
    
    # 传感器数据采集命令
    ADC_SINGLE_READ = 0x30
    ADC_64CH_COLLECT = 0x40
    ADC_64CH_SEND = 0x41
    RAW_64CH_COLLECT = 0x50
    RAW_64CH_PRESSURE_SEND = 0x51
    RAW_64CH_TEMP_SEND = 0x52
    I2C_64CH_RAW_COLLECT = 0x53
    I2C_64CH_CAL_COLLECT = 0x54
    OWI_64CH_CAL_COLLECT = 0x60
    OWI_64CH_PRESSURE_SEND = 0x61
    OWI_64CH_TEMP_SEND = 0x62
    NSA5312_64CH_COLLECT = 0x63
    
    # 校准和配置命令
    OWI_CALIBRATION_MODE = 0x23

class StatusCode(Enum):
    """状态代码枚举"""
    SUCCESS = 0x00
    CRC_ERROR = 0xF0
    OPERATION_FAILURE = 0xF1

class NSA5312Protocol:
    """NSA5312串口协议处理类"""
    
    # 协议常量
    DEVICE_ADDRESS = 0xE1
    FIRMWARE_VERSION = 17
    CRC_POLYNOMIAL = 0x07
    MAX_FRAME_SIZE = 200
    
    def __init__(self):
        self.lock = threading.Lock()
        
    def calculate_crc(self, data: bytes) -> int:
        """
        计算CRC校验码
        生成多项式：0x07 (x^8 + x^2 + x + 1)
        """
        crc = 0
        for byte in data:
            crc ^= byte
            for _ in range(8):
                if crc & 0x80:
                    crc = (crc << 1) ^ self.CRC_POLYNOMIAL
                else:
                    crc = crc << 1
                crc &= 0xFF
        return crc
    
    def build_command_frame(self, command: int, parameters: bytes = b'') -> bytes:
        """
        构建命令帧
        格式：[地址][长度][命令][参数...][CRC]
        """
        frame_length = 4 + len(parameters)  # 地址 + 长度 + 命令 + CRC + 参数
        frame_data = struct.pack('BBB', self.DEVICE_ADDRESS, frame_length, command)
        frame_data += parameters
        
        # 计算CRC（包含地址字节）
        crc = self.calculate_crc(frame_data)
        frame_data += struct.pack('B', crc)
        
        return frame_data
    
    def parse_response_frame(self, data: bytes) -> Tuple[bool, int, int, bytes]:
        """
        解析响应帧
        格式：[地址][长度][命令][状态][数据...][CRC]
        返回：(是否成功, 命令码, 状态码, 数据)
        """
        if len(data) < 5:
            return False, 0, StatusCode.CRC_ERROR.value, b''
            
        address, length, command, status = struct.unpack('BBBB', data[:4])
        
        if address != self.DEVICE_ADDRESS:
            return False, 0, StatusCode.CRC_ERROR.value, b''
            
        if length != len(data):
            return False, 0, StatusCode.CRC_ERROR.value, b''
            
        # 验证CRC
        received_crc = data[-1]
        calculated_crc = self.calculate_crc(data[:-1])  # 包含地址字节
        
        if received_crc != calculated_crc:
            return False, command, StatusCode.CRC_ERROR.value, b''
            
        response_data = data[4:-1] if len(data) > 5 else b''
        return True, command, status, response_data
    
    def build_get_version_command(self) -> bytes:
        """构建获取版本信息命令"""
        return self.build_command_frame(CommandCode.GET_VERSION.value)
    
    def build_gpio_control_command(self, command_type: CommandCode, channel: int) -> bytes:
        """构建GPIO控制命令"""
        params = struct.pack('B', channel)
        return self.build_command_frame(command_type.value, params)
    
    def build_adc_single_read_command(self, channel: int) -> bytes:
        """构建单通道ADC读取命令"""
        params = struct.pack('B', channel)
        return self.build_command_frame(CommandCode.ADC_SINGLE_READ.value, params)
    
    def build_i2c_command(self, is_read: bool, device_addr: int, reg_addr: int, data: bytes = b'') -> bytes:
        """构建I2C命令"""
        if is_read:
            params = struct.pack('BBB', device_addr, reg_addr, len(data))
            command = CommandCode.I2C_READ.value
        else:
            params = struct.pack('BB', device_addr, reg_addr) + data
            command = CommandCode.I2C_WRITE.value
        return self.build_command_frame(command, params)
    
    def build_spi_command(self, is_read: bool, data: bytes, is_spi4: bool = False) -> bytes:
        """构建SPI命令"""
        if is_spi4:
            command = CommandCode.SPI4_READ.value if is_read else CommandCode.SPI4_WRITE.value
        else:
            command = CommandCode.SPI_READ.value if is_read else CommandCode.SPI_WRITE.value
        
        params = struct.pack('B', len(data)) + data
        return self.build_command_frame(command, params)
    
    def build_owi_command(self, is_read: bool, data: bytes) -> bytes:
        """构建OWI命令"""
        command = CommandCode.OWI_READ.value if is_read else CommandCode.OWI_WRITE.value
        params = struct.pack('B', len(data)) + data
        return self.build_command_frame(command, params)
    
    def build_64ch_collect_command(self, command_type: CommandCode) -> bytes:
        """构建64通道数据采集命令"""
        return self.build_command_frame(command_type.value)
    
    def build_custom_command(self, command: int, parameters: bytes = b'') -> bytes:
        """构建自定义命令"""
        return self.build_command_frame(command, parameters)
    
    def parse_adc_data(self, data: bytes) -> List[int]:
        """解析ADC数据"""
        adc_values = []
        for i in range(0, len(data), 2):
            if i + 1 < len(data):
                value = struct.unpack('>H', data[i:i+2])[0]  # 大端序16位
                adc_values.append(value)
        return adc_values
    
    def parse_temperature_data(self, data: bytes) -> float:
        """解析温度数据"""
        if len(data) >= 2:
            raw_temp = struct.unpack('>H', data[:2])[0]
            # 根据NSA5312规格书：温度 = (TADC * 0.077) + 51.6
            if raw_temp & 0x8000:  # 负数处理
                raw_temp = raw_temp - 65536
            temperature = (raw_temp * 0.077) + 51.6
            return temperature
        return 0.0
    
    def format_hex_data(self, data: bytes) -> str:
        """格式化十六进制数据显示"""
        return ' '.join(f'{b:02X}' for b in data)
    
    def validate_frame_format(self, data: bytes) -> bool:
        """验证帧格式是否正确"""
        if len(data) < 4:
            return False
        
        address = data[0]
        length = data[1]
        
        return (address == self.DEVICE_ADDRESS and 
                length == len(data) and 
                length <= self.MAX_FRAME_SIZE)

class NSA5312Calibration:
    """NSA5312校准功能类"""
    
    def __init__(self):
        self.temperature_points = []  # 温度点数据
        self.gain_coefficients = []   # 增益系数
        self.zero_coefficients = []   # 零点系数
        
    def add_calibration_point(self, temperature: float, gain: float, zero: float):
        """添加校准点"""
        self.temperature_points.append(temperature)
        self.gain_coefficients.append(gain)
        self.zero_coefficients.append(zero)
        
    def calculate_linear_interpolation(self, current_temp: float) -> Tuple[float, float]:
        """
        计算线性插值
        返回：(增益系数, 零点系数)
        """
        if len(self.temperature_points) < 2:
            return 1.0, 0.0
            
        # 找到温度区间
        for i in range(len(self.temperature_points) - 1):
            if self.temperature_points[i] <= current_temp <= self.temperature_points[i + 1]:
                # 线性插值计算
                t1, t2 = self.temperature_points[i], self.temperature_points[i + 1]
                g1, g2 = self.gain_coefficients[i], self.gain_coefficients[i + 1]
                z1, z2 = self.zero_coefficients[i], self.zero_coefficients[i + 1]
                
                ratio = (current_temp - t1) / (t2 - t1)
                gain = g1 + (g2 - g1) * ratio
                zero = z1 + (z2 - z1) * ratio
                
                return gain, zero
        
        # 超出范围时使用最近的点
        if current_temp < self.temperature_points[0]:
            return self.gain_coefficients[0], self.zero_coefficients[0]
        else:
            return self.gain_coefficients[-1], self.zero_coefficients[-1]
    
    def calculate_dac_value(self, coefficient: float, is_gain: bool = True) -> int:
        """
        计算DAC控制字
        增益DAC：1/3 到 1 对应 0x0000 到 0x3FFF
        零点DAC：0 到 VEXC 对应 0x0000 到 0x3FFF
        """
        if is_gain:
            # 增益DAC：Gain = 1/3 + 2/3 * (DAC/16383)
            dac_value = int((coefficient - 1/3) * 16383 / (2/3))
        else:
            # 零点DAC：线性映射
            dac_value = int(coefficient * 16383)
            
        return max(0, min(16383, dac_value))
    
    def export_calibration_data(self) -> dict:
        """导出校准数据"""
        return {
            'temperature_points': self.temperature_points,
            'gain_coefficients': self.gain_coefficients,
            'zero_coefficients': self.zero_coefficients
        }
    
    def import_calibration_data(self, data: dict):
        """导入校准数据"""
        self.temperature_points = data.get('temperature_points', [])
        self.gain_coefficients = data.get('gain_coefficients', [])
        self.zero_coefficients = data.get('zero_coefficients', [])